///
/// $Id: serialize.cpp,v 1.10 2006/06/04 15:38:02 olegus Exp $
///

#include "cmain.h"
#include "serialize.h"

ArchiveVersion_t ARCHIVE_VERSION_MODEL = MAKE_ARCHIVE_VERSION( 1, 3 );

/////////////////////////////////////////////////////////////////////////////

PointerCache_c::PointerCache_c()
:   m_iPointer( 0 )
{
}

PointerCache_c::~PointerCache_c()
{
    Reset();
}

int PointerCache_c::VoidPtrToInt( void * pPointer )
{
    if( pPointer == NULL )
        return -1;

    return m_cPtrToInt.find( pPointer ) == m_cPtrToInt.end() 
         ? NOT_FOUND 
         : m_cPtrToInt[ pPointer ];
}

void * PointerCache_c::IntToVoidPtr( int iPointer )
{
    if( iPointer == -1 )
        return NULL;

    return m_cIntToPtr.find( iPointer ) == m_cIntToPtr.end() 
         ? reinterpret_cast< void* > ( NOT_FOUND )
         : m_cIntToPtr[ iPointer ];
}

void PointerCache_c::Reset()
{
    m_cIntToPtr.clear();
    m_cPtrToInt.clear();
    m_iPointer = 0;
}

bool PointerCache_c::IsInCache( void * pPointer )
{
    return VoidPtrToInt( pPointer ) != NOT_FOUND;
}

bool PointerCache_c::IsInCache( int iPointer )
{
    return IntToVoidPtr( iPointer ) != reinterpret_cast< void* > ( NOT_FOUND );
}

void PointerCache_c::SetVoid( int iPointer, void * pPointer )
{
    m_cIntToPtr[ iPointer ] = pPointer;
    m_cPtrToInt[ pPointer ] = iPointer;
}

bool PointerCache_c::AddVoid( int & iPointer, void * pPointer )
{
    iPointer = VoidPtrToInt( pPointer );
    if( iPointer == NOT_FOUND )
    {
        iPointer = m_iPointer;
        m_cIntToPtr[ iPointer ] = pPointer;
        m_cPtrToInt[ pPointer ] = iPointer;
        m_iPointer++;
        return true;
    }
    return false;
}

////////////////////////////////////////////////////////////////////////////

Serializable_c::Serializable_c( Stream_c& cStream, bool isReading, ArchiveVersion_t tVersion )
:   m_cStream  ( &cStream  )
,   m_isReading( isReading )
,   m_tVersion ( tVersion )
{
    (*this) << m_tVersion;
}

Serializable_c::~Serializable_c()
{
}

ArchiveVersion_t Serializable_c::GetVersion()
{
    return m_tVersion;
}

void Serializable_c::Serialize( void *pData, int nDataBytes )
{
    int nBytes = 0;
    if( m_isReading ) nBytes = m_cStream->Read ( pData, nDataBytes );
    else              nBytes = m_cStream->Write( pData, nDataBytes );
    Assert( nBytes == nDataBytes && "io failed");
}

/////////////////////////////////////////////////////////////////////////////

Serializer_c::SerializerArray_t         * Serializer_c::m_dSerializers = NULL;
PointerCache_c                          Serializer_c::m_cPointerCache;

Serializer_c::Serializer_c( const char * sClassName )
{
    strcpy( m_sClassName, sClassName );

    if( m_dSerializers == NULL )
        m_dSerializers = new SerializerArray_t;
    (*m_dSerializers).Add( this );
}

Serializer_c::~Serializer_c()
{
    SafeDelete( m_dSerializers );
}

Serializer_c * Serializer_c::FindSerializer( const char * sClassName )
{
    ARRAY_FOREACH( i, (*m_dSerializers) )
    {
        if( strcmp( sClassName, (*m_dSerializers)[i]->m_sClassName ) == 0 )
            return (*m_dSerializers)[i];
    }
    return NULL;
}

void Serializer_c::ReadPointer( Serializable_c & Archive, void * & pPointer )
{
    int iPointer;
    Archive << iPointer;
    if( ! m_cPointerCache.IsInCache( iPointer ) )
    {
        ClassName_t sLoadClassName;
        Archive << sLoadClassName;
        Serializer_c * pSerializer = FindSerializer( sLoadClassName );
        Assert( pSerializer && "no serializer for class" );
        pSerializer->SerializeObject( Archive, pPointer );
        m_cPointerCache.Set( iPointer, pPointer );
    }
    else
    {
        pPointer = m_cPointerCache.IntToVoidPtr( iPointer );
    }
}

void Serializer_c::WritePointer( Serializable_c & Archive, void * & pPointer )
{
    int iPointer;
    if( m_cPointerCache.Add( iPointer, pPointer ) )
    {
        Archive << iPointer;
        Archive << m_sClassName;
        SerializeObject( Archive, pPointer );
    }
    else
    {
        Archive << iPointer;
    }
}

void Serializer_c::RegisterPointer( Serializable_c & Archive, void * pPointer )
{
    int iPointer = -1;

    if( Archive.IsReading() )
    {
        Archive << iPointer;
        if( ! m_cPointerCache.IsInCache( iPointer ) )
            m_cPointerCache.SetVoid( iPointer, pPointer );
    }
    else
    {
        m_cPointerCache.AddVoid( iPointer, pPointer );
        Archive << iPointer;
    }
}

///
/// $Id: serialize.cpp,v 1.10 2006/06/04 15:38:02 olegus Exp $
///
